/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 * Description: To improve the compression ratio of squashfs, the new mksquashfsex user-mode tool is used to modify
 * the compression mode to create a new file system ext-squashfs,
 * The function of interpreting ext-squashfs is added to the kernel squashfs module as follows.
 * Author: wangzirui
 * Create: 2022-08-05
 */
#ifndef SQUASHFS_EX_H
#define SQUASHFS_EX_H

#include <linux/fs.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/mm_types.h>
#include <linux/init.h>

#include "squashfs_fs_sb.h"

#define SQUASHFS_EX_FRAG 0xFFFFFFFEU
#define SQUASHFS_EX_FLAG (1 << 15)
#define EXSQUASHFS_MAGIC 0x4558535155415348

#define SQUASHFS_EX_SMALL_BLOCK_FLAG (1U << 1)
#define SQUASHFS_EX_SMALL_BLOCK_BIT (1U << 30)
#define SQUASHFS_EX_COMPRESSED_BIT (1U << 31)
#define SQUASHFS_EX_RESERVED_BYTE 6
#define PREFETCH_SIZE_MAX (256 * 1024)
#define SQUASHFS_ALIGN_MASK 4096
#define SQUASHFS_EX_CACHE_MAX (1U << 26)
#define BUFFER_SIZE 1024
#define EX_MIN_BLOCK_LOG 16
#define EX_MAX_BLOCK_LOG 26
#define EX_MIN_SMALL_BLOCK_LOG 16
#define EX_MAX_SMALL_BLOCK_LOG 20

/*
 * In-ram data for ex-squashfs super block.
 */
struct ex_super_block_info {
	spinlock_t read_page_lock;              // lock for updating read_page
	struct squashfs_cache *read_page;       // cache for block
	struct squashfs_cache *small_read_page; // cache for small block
	u64 blocks_start;                       // start address for blocks
	u64 blocks;                             // block count
	u64 chunks_start;                       // start address for chunks
	u64 chunk_table_start;                  // start address for chunk table
	u32 chunk_table_size;                   // size of chunk table
	u32 chunks_num;                         // chunk count
	u32 *chunk_table;                       // starting chunk index of file
	u32 *block_info_list;                   // list of compressed block size and block flags
	u64 *block_addr_list;                   // list of block address
	u32 block_size;                         // size of a data block
	u32 small_block_size;                   // size of a small data block
	u16 flags;                              // ex-squashfs flags
};

/*
 * page_reader marked one chunk that need to be read into the specific page,
 * providing information needed for underlying block operations.
 */
struct page_reader {
	u64 block_addr;   // Block addr to read
	u32 block_info;   // Block size along with compressed flag and small flag
	u32 block_offset; // Data position within decompressed block data
	u32 block_id;     // Block index
	u64 page_index;   // Page index to be read into
	u32 page_offset;  // Data position within page
	u32 read_size;    // Size of this read operation
	struct list_head node_handle;
};

/*
 * On-disk data for ex-squashfs super block.
 * All of which are in little-endian. Conversion is needed for accessing it's
 * content.
 */
struct __packed ex_super_block {
	__le64 magic;                             // 'EXSQUASH'
	__le16 block_size_log2;                   // size of normal data block, log2
	__le16 small_block_size_log2;             // size of small data block, log2
	__le16 compression;                       // unused
	__le16 flags;                             // flags for ex-squashfs
	u8 s_major;                               // unused
	u8 s_minor;                               // unused
	u8 __reserved[SQUASHFS_EX_RESERVED_BYTE]; // reserved field
	__le64 chunk_table_start;                 // start address for chunk_tabls
	__le64 chunks_start;                      // start address for chunks
	__le64 blocks_start;                      // start address for data blocks
	__le64 blocks;                            // block count
	__le32 checksum;                          // crc32 of header
};

/*
 * On-disk data for one chunk instance, which stores a fragment of data within
 * a file.
 */
struct raw_chunk {
	__le32 block;  // Which block this chunk is in
	__le32 size;   // How big is this chunk.
	__le32 offset; // Where's the data within decompressed block.
};

/*
 * In-ram data for one chunk instance, which stores a fragment of data within
 * a file.
 */
struct chunk {
	u32 block;  // Which block this chunk is in
	u32 size;   // How big is this chunk.
	u32 offset; // Where's the data within decompressed block.
};

/*
 * Multiple chunks, typically representing a whole file.
 */
struct chunk_info {
	u32 count;
	struct chunk *chunks;
};

extern void squashfs_ex_put_super(struct ex_super_block_info *ex_sb_info);
extern struct ex_super_block_info *squashfs_ex_fill_super(struct super_block *sb);
extern int squashfs_ex_readpage(struct file *file, struct page *page);
extern struct chunk_info *squashfs_ex_get_chunk_info(struct super_block *sb,
	u32 chunk_table_index, loff_t inode_size);

int __init squashfs_ex_init(void);
void squashfs_ex_destroy(void);
int read_datablock(struct super_block *sb, u64 index, u32 block_info, u64 *next_index,
	struct squashfs_page_actor *output);

#endif
